Conversation
3a92db6 to
4cbcb92
Compare
4cbcb92 to
3e7e049
Compare
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
3e7e049 to
7de19dd
Compare
Regression tests for incoming changes for AIT-685. Guard the contract that unchanged messages keep stable object references during streaming updates. These tests pass today and protect against regressions as we optimize the View's update path: - View: unchanged message refs preserved after content-only upsert - View: array ref changes after update (so React detects it) - View: simulated streaming — only the active message ref changes - useView: message refs stable across mock streaming updates - useMessageSync: message refs stable across mock streaming updates
During token streaming, every tree update caused multiple O(n) tree
traversals: once inside the View's _onTreeUpdate(), then again for
each React hook subscriber calling view.flattenNodes(). With rapid
streaming tokens this multiplied into a significant performance cost.
The View now caches the flattenNodes() result in a _cachedNodes field.
The public flattenNodes() returns this cache in O(1). A new private
_computeFlatNodes() method performs the actual tree walk and is called
only when the visible output may have changed: tree structural changes,
branch selection changes, fork auto-selection, and history page reveal.
The original design avoided caching ("no cache invalidation complexity,
at the cost of repeated traversals") because the result depends on
branch selection state. Caching is safe here because every mutation
path that can change the visible output refreshes the cache
synchronously before emitting events to consumers - JS single-threading
eliminates async staleness risks.
During token streaming, every tree update triggered a full O(n) tree walk even though the tree structure hadn't changed - only a single message's content was updated. With conversations of hundreds of messages and tokens arriving at high frequency, this became the dominant cost on the hot path. The Tree now exposes a structuralVersion counter (on TreeInternal) that increments on insert, delete, and serial promotion - but not on content-only upsert of an existing node. The View compares this counter against its last-seen version: when unchanged, it takes a fast path that compares cached message references against the snapshot in O(visible_count) instead of re-walking the tree. A monotonic counter was chosen over alternatives (tagged events, dirty sets, boolean flags) because it requires no public API change, is multi-View safe (each View tracks its own last-seen version), and cannot produce false negatives.
7de19dd to
324ff7f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
flattenNodes()in the View so React hooks get O(1) lookupsinstead of re-walking the entire tree on every streaming token
structuralVersioncounter to the Tree so the View candistinguish content-only updates (streaming) from structural changes
(new nodes, deletions, branch reorders) and skip the tree walk entirely
React.memo can skip re-rendering them
Resolves AIT-685